#pragma once
#include <Dynamics/Material/FemMaterial/fem_linear.h>
#include <Dynamics/Material/FemMaterial/fem_corotated.h>
#include <Dynamics/Material/FemMaterial/fem_neohookean.h>
#include <Dynamics/Material/FemMaterial/fem_stvk.h>

namespace PhysLeo {

/**
 * Fem material type
 * now contains linear, corotated linear, stvk, neohookean. future will contain spline nonlinear [barbic's paper]
 */
enum class FemMaterialType
{
	LINEAR, COROTATED, STVK, NEOHOOKEAN
};

/**
 * static class describes the interface of FEM material.
 * fem material accept a deformation gradient as argument, then compute the energy density
 * or first piola stress. Different material may need different number of material parameters.
 */
template<typename T>
class FemMaterial {
public:
    /**
     * return the energy density according to deformation gradient
     * @param[in] F  a glm 3x3 matrix type represents the deformation gradient.
     * @param[in] ptr_material  a pointer to vertex specific material data
     * @param[in] type  fem material type
     * @return a T value, the energy density.
     */
    __host__ __device__ static T energyDensity(glm::tmat3x3<T> F, T* ptr_material, const FemMaterialType type)
    {
        switch (type)
        {
        case FemMaterialType::LINEAR:
        {
            return FemLinear<T>::energyDensity(F, ptr_material);
        }
        case FemMaterialType::COROTATED:
        {
            return FemCorotated<T>::energyDensity(F, ptr_material);
        }
        case FemMaterialType::STVK:
        {
            return FemStvk<T>::energyDensity(F, ptr_material);
        }
        case FemMaterialType::NEOHOOKEAN:
        {
            return FemNeohookean<T>::energyDensity(F, ptr_material);
        }
        default:
        {
            return FemLinear<T>::energyDensity(F, ptr_material);
        }
        }
    }

    /**
     * return the first piola stress according to deformation gradient.
     * @param[in] F  a glm 3x3 matrix type represents the deformation gradient.
     * @param[in] ptr_material  a pointer to vertex specific material data
     * @param[in] type  fem material type
     * @return a glm 3x3 matrix type represents the first piola stress.
     */
	__host__ __device__ static glm::tmat3x3<T> firstPiolaStress(glm::tmat3x3<T> F, T* ptr_material, const FemMaterialType type)
    {
        switch (type)
        {
        case FemMaterialType::LINEAR:
        {
            return FemLinear<T>::firstPiolaStress(F, ptr_material);
        }
        case FemMaterialType::COROTATED:
        {
            return FemCorotated<T>::firstPiolaStress(F, ptr_material);
        }
        case FemMaterialType::STVK:
        {
            return FemStvk<T>::firstPiolaStress(F, ptr_material);
        }
        case FemMaterialType::NEOHOOKEAN:
        {
            return FemNeohookean<T>::firstPiolaStress(F, ptr_material);
        }
        default:
        {
            return FemLinear<T>::firstPiolaStress(F, ptr_material);
        }
        }
    }

    /**
    * return the number of material parameters for different fem material type
    * @param fem_material_type
    * @return the number of material parameters
    */
    __host__ __device__ static int numMaterialParams(const FemMaterialType fem_material_type)
    {
        switch (fem_material_type)
        {
        case FemMaterialType::LINEAR:return 2;
        case FemMaterialType::STVK:return 2;
        case FemMaterialType::NEOHOOKEAN:return 2;
        case FemMaterialType::COROTATED:return 2;
        default:return 2;
        }
    }
};

}